#include "matrix.h"
#include "GICv3.h"
#include "interrupt.h"
#include "gtimer.h"
#include "timer.h"
#include "device.h"
#include "thread.h"

/* gtimer interrupt id */
#define GTIMER_INTR_ID 27

/* get cpu affinity */
extern mx_uint32_t GetAffinity();
/* the number of timer ticks required for each system frequency */
volatile static mx_uint64_t increase_val;
/* device descriptor of gtimer */
static mx_device_t dev_gtimer;

/* set the target factor and clear the interrupt flag of gtimer hardware */
static inline void gtimer_set_clr(mx_uint64_t increase_val)
{
    __asm__ volatile("msr CNTV_TVAL_EL0, %0"::"r"(increase_val));
}

/* gtimer interrupt handle */
static void gtimer_intr_handler (mx_void_pt param)
{
    /* system soft timer */
    mx_timer_tick();
    /* clear the gtimer interrupt flag and reload tick */
    gtimer_set_clr(increase_val);
}

static mx_device_ops_t ops = { /* no operations */ };

static mx_device_data_t devdata = {
    .name = "gtimer",
    .drvname = ""
};

/* device gtimer initialization */
void device_gtimer_init ()
{
    mx_uint64_t val;
    /* get the frequency of gtimer */
    __asm__ volatile("mrs %0, CNTFRQ_EL0":"=r"(val));
    increase_val = val / MX_TIMER_PERIOD;

    mx_uint32_t gicr = GetGICR(GetAffinity());
    SetPrivateIntPriority(gicr, GTIMER_INTR_ID, 0);
    SetPrivateIntSecurity(gicr, GTIMER_INTR_ID, gicigroupr_G1NS);
    EnablePrivateInt(gicr, GTIMER_INTR_ID);
    
    /* register gtimer interrupt handler to system */
    mx_interrupt_register("gtimer-int27", GTIMER_INTR_ID, gtimer_intr_handler, mx_null);
    /* register gtimer device to system */
    mx_device_mount(&dev_gtimer, &devdata, &ops);
    
    gtimer_set_clr(increase_val);
    /* enable gtimer */
    setEL1VirtualTimerCtrl(CNTV_CTL_ENABLE);
}
MX_BOARDINIT_EXPORT(device_gtimer_init, 1);
